Skip to content

Fast CAGRA Index Merge - #2352

Open
landrumb wants to merge 48 commits into
NVIDIA:mainfrom
landrumb:landrumb/cagra-fastener-merge
Open

Fast CAGRA Index Merge#2352
landrumb wants to merge 48 commits into
NVIDIA:mainfrom
landrumb:landrumb/cagra-fastener-merge

Conversation

@landrumb

@landrumb landrumb commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

This PR implements the Fastener graph merge operation. This PR supports merging for float, half, int8, and uint8 dtypes, and euclidean distances.

There was originally specialization to use int8 GEMM for the integer types, but I ran into portability issues on Ada and it turns out that using the same f32 path for both is simpler and not substantially slower. Currently investigating switching the unified path to TF32 to use tensor cores.

The core logic resides in cagra_merge_scaffold.cuh.

This PR adds 7.64 MiB to libcuvs.so, a 2.94% increase.

image (H100) image image image image image

@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@copy-pr-bot

copy-pr-bot Bot commented Jul 22, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@dantegd

dantegd commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

/ok to test 2c54392

@aamijar aamijar added non-breaking Introduces a non-breaking change feature request New feature or request labels Jul 22, 2026
@aamijar aamijar moved this to In Progress in Unstructured Data Processing Jul 22, 2026
@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb

Copy link
Copy Markdown
Contributor Author

/ok to test

@landrumb

Copy link
Copy Markdown
Contributor Author

Currently failing because the wheel builds are too big. Not sure there's much I can do about this.

@achirkin achirkin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this extensive contribution! Since the datataset API changes are in, the main request is to rethink where we allocate the new, consolidated dataset during merge (see below).
Then, please try to refactor the code to use raft mdarrays and mdspans in place of RMM and use the workspace/large_workspace resources for all temporary allocations - this will help us to keep track of memory requirements (which seem to be plenty).

Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment on lines +91 to +103
/** Device state and tuning knobs shared by every split level: the precomputed row norms, the
* tiling and workspace capacities, and the seed feeding the deterministic leader samples. */
struct split_context {
split_context(rmm::cuda_stream_view stream, int64_t rows)
: norms(static_cast<size_t>(rows), stream)
{
}

rmm::device_uvector<float> norms;
int assignment_tile_rows = ASSIGNMENT_TILE_ROWS;
size_t gemm_workspace_bytes = GEMM_WORKSPACE_BYTES;
uint64_t seed = DETERMINISTIC_SEED;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try to switch to raft primitives here: raft::device_vector<float> as the buffer and raft::resources as an argument.
Then, we need to consider: does this context fit into the bounded workspace memory or does it grow with the problem size unbounded? Depending on the answer, use raft::get_workspace_resource_ref or raft::get_large_workspace_resource_ref.
By doing this, we let the user control where the working memory for the algorithm resides, enable better accounting (via tools like raft::memory_tracking_resources), and prepare for the dry run execution (feature soon to be enabled in raft).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone through and I think this is done correctly now, let me know if I misunderstand the usage of the raft allocs. Is there a way I should be testing preparedness for dry run execution?

Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment on lines +399 to +407
if (params.attach_dataset_on_build) {
// CAGRA search assumes 16-byte row alignment (vectorized loads). Consolidate remains dense for
// the scaffold; attach through make_aligned_dataset so unaligned dims are padded.
merged_index.update_dataset(handle, make_aligned_dataset(handle, std::move(dataset), 16));
} else {
using ds_idx_type = typename index<T, IdxT>::dataset_index_type;
merged_index.update_dataset(
handle, std::make_unique<cuvs::neighbors::empty_dataset<ds_idx_type>>(preflight.dim));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, we should move the dataset consolidation into a separate place, perhaps as a part of the dataset API. CC @HowardHuang1 to discuss what would be the place for dataset merging under the new API and who should own it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make an issue for this, but could we leave it here for now?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding offline context to the thread:
The dataset merging logic now present is just copying vectors into storage that's now provided by the caller per interface changes that were introduced with the dataset API. The rebuild version of this takes a bitmap to filter them, and the fastener version doesn't.

Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
landrumb and others added 5 commits August 3, 2026 10:23
Co-authored-by: Artem M. Chirkin <9253178+achirkin@users.noreply.github.com>
Co-authored-by: Artem M. Chirkin <9253178+achirkin@users.noreply.github.com>
@landrumb
landrumb requested a review from dantegd August 4, 2026 16:44
@landrumb
landrumb force-pushed the landrumb/cagra-fastener-merge branch from 1b8558a to 653e8a0 Compare August 4, 2026 20:20
@landrumb
landrumb requested a review from achirkin August 5, 2026 17:50
rapids-bot Bot pushed a commit that referenced this pull request Aug 6, 2026
The graph merge PR (#2352) uses some of the kernels defined by CAGRA, and binary size takes a hit from them not appearing in a TU where they can be shared by both CAGRA and Fastener.

Authors:
  - Ben Landrum (https://github.com/landrumb)

Approvers:
  - Divye Gala (https://github.com/divyegala)

URL: #2412

@dantegd dantegd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have only the small concerns, besides it things look solid!

Also, it looks like a few working artifacts may have slipped into the PR that need to be removed before merge: PR_2352_REVIEW_WORK.md, pipnn.pdf and FASTENER_PR_PLAN.md, right?

Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment on lines +3488 to +3489
* `cuvs::neighbors::cagra::detail::merged_dataset_size`.
* @param[in] row_filter Optional row filter. Any filter selects rebuild in AUTO and is rejected by

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can users actually use this sizing instruction from the installed C++ API?

merged_dataset_size() is defined under cpp/src and isn’t declared in the public header. Could we either expose an appropriate public helper?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking more at it I'm not super convinced this is a useful enough helper to expose. It just gives the number of rows in the output, which is either a popcount of the filter or the sum of the inputs, and leaves the user to extract the dimension and stride to figure out the size of the alloc. I think it makes more sense to either surface a helper that gives the size and/or shape of the allocation or surface no helper at all.

Comment on lines +3452 to +3461
struct merge_params {
merge_algo algo = merge_algo::AUTO;
uint32_t levels = 2;
uint32_t root_fanout = 2;
uint32_t lower_fanout = 3;
double leader_fraction = 0.02;
uint32_t max_leaders = 1024;
uint32_t leaf_size = 256;
uint32_t leaf_degree = 4;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to add a section in the documentation on these parameters and their typical values.

Comment thread c/src/neighbors/cagra.cpp
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge.cuh Outdated
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh Outdated
Comment thread cpp/src/neighbors/detail/cagra/cagra_merge_scaffold.cuh
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request New feature or request non-breaking Introduces a non-breaking change

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

5 participants